package org.folio.rest.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import io.vertx.core.AsyncResult;
import io.vertx.core.Context;
import io.vertx.core.Future;
import io.vertx.core.Handler;
import io.vertx.core.json.JsonArray;
import io.vertx.core.logging.Logger;
import io.vertx.core.logging.LoggerFactory;
import org.folio.cql2pgjson.CQL2PgJSON;
import org.folio.cql2pgjson.exception.FieldException;
import org.folio.rest.RestVerticle;
import org.folio.rest.impl.other.PartyLogSave;
import org.folio.rest.impl.other.PartyUtil;
import org.folio.rest.impl.other.blacklist.BlacklistService;
import org.folio.rest.impl.other.take.TenantsTaskService;
import org.folio.rest.impl.pojo.AttendExportObject;
import org.folio.rest.impl.util.FileExport;
import org.folio.rest.jaxrs.model.*;
import org.folio.rest.jaxrs.resource.PartyAttend;
import org.folio.rest.persist.PgUtil;
import org.folio.rest.persist.PostgresClient;
import org.folio.rest.persist.cql.CQLWrapper;
import org.folio.rest.tools.messages.MessageConsts;
import org.folio.rest.tools.messages.Messages;
import org.folio.rest.tools.utils.ValidationHelper;

import javax.ws.rs.core.Response;
import java.util.*;

import static io.vertx.core.Future.succeededFuture;

/**
 * @author lee
 * @Classname AttendImpl
 * @Description TODO
 * @Date 2020/6/18 16:21
 * @Created by lee
 */
public class AttendImpl implements PartyAttend {
    private final Logger logger = LoggerFactory.getLogger("modparty");
    private final Messages messages = Messages.getInstance();
    private static final String NOTIFY_TABLE = "attend";


    private CQLWrapper getCQL(String query, int limit, int offset) throws FieldException {
        return new CQLWrapper(new CQL2PgJSON(NOTIFY_TABLE + ".jsonb"), query, limit, offset);
    }
    private String internalErrorMsg(Exception e, String lang) {
        if (e != null) {
            logger.error(e.getMessage(), e);
        }
        String message = messages.getMessage(lang, MessageConsts.InternalServerError);
        if (e != null && e.getCause() != null && e.getCause().getClass().getSimpleName()
                .endsWith("CQLParseException")) {
            message = " CQL parse error " + e.getLocalizedMessage();
        }
        return message;
    }

    @Override
    public void postPartyAttend(String lang, org.folio.rest.jaxrs.model.Attend entity, Map<String, String> okapiHeaders, Handler<AsyncResult<Response>> asyncResultHandler, Context vertxContext) {
        List<ReaderReserveGroup > list = entity.getReaderReserveGroup();
        PostgresClient pg = PgUtil.postgresClient(vertxContext, okapiHeaders);
        if (list !=null && list.size() ==1)
        {

            String reserveId = UUID.randomUUID().toString();
            Date reserveDate = new Date();
            Reserve reserve = new Reserve();
            String reserveDateString = DateUtil.formatDateTime(reserveDate);
            reserve.setIsDel(0);
            reserve.setPropertyName(entity.getPropertyName());
            reserve.setReaderId(entity.getReaderReserveGroup().get(0).getBarcode());
            reserve.setIsCheckIn("true");

            reserve.setPartyId(entity.getPartyId());
            reserve.setReaderName(entity.getReaderReserveGroup().get(0).getName());
            reserve.setPartyName(entity.getPartyName());
            reserve.setStatus(1);
            reserve.setReaderReserveGroup(entity.getReaderReserveGroup());
            reserve.setId(reserveId);


            entity.setReserveDate(reserveDateString);
            pg.getById("party",entity.getPartyId(),PartyGroup.class,reply->{
                if (reply.succeeded())
                {
                    PartyGroup party = reply.result();
                    reserve.setPartyVenue(party.getVenue());
                    reserve.setPartyStartDate(party.getPartyStartDate());
                    reserve.setPartyEndDate(party.getPartyEndDate());
                    reserve.setPartyCategory(""+party.getCategory());
                    reserve.setNotes("system");
                    reserve.setChannel(1);
                    reserve.setReserveDate(reserveDateString);

                    reserve.setImageId(party.getImageId());
                    reserve.setMetadata(entity.getMetadata());
                    pg.save("reserve",reserve,reserveReply->{
                        if(reserveReply.succeeded()){

                            entity.setReserveId(reserveReply.result());

                            pg.save(NOTIFY_TABLE,entity,attendReply->{
                                if(attendReply.succeeded()){
                                    TenantsTaskService.setJob(okapiHeaders.get(RestVerticle.OKAPI_HEADER_TENANT),"blacklistService",entity);
                                    //BlacklistService.recordBehaviorAndCheck(entity);
                                    asyncResultHandler.handle(succeededFuture(PostPartyAttendResponse.respond201WithApplicationJson(entity,PostPartyAttendResponse.headersFor201())));
                                    PartyLogSave.saveLog("新增活动名称为【"+ party.getPartyName() +"】读者证号为【"+entity.getReaderReserveGroup().get(0).getBarcode()+"】的记录",2,entity.getMetadata(),entity.getOperator(),okapiHeaders,asyncResultHandler,pg);
                                }
                            });
                        }
                    });
                }
            });
        }else if (entity.getIdArray() !=null && entity.getIdArray().length()>0)
        {
            String idString = entity.getIdArray();
            String[] str = idString.split(",");
            JsonArray jsonArray = new JsonArray(Arrays.asList(str));
            pg.getById(NOTIFY_TABLE,jsonArray,Attend.class, reply->{
                if (reply.succeeded())
                {
                    Map<String, Attend> attendGroup = reply.result();
                    Attend attend =attendGroup.get(str[0]);
                    attendGroup.forEach((a,b)->{
                        b.setAttendState(entity.getAttendState());
                        b.setOperator(entity.getOperator());
                        pg.update(NOTIFY_TABLE,b,a,updateReply->{
                            TenantsTaskService.setJob(okapiHeaders.get(RestVerticle.OKAPI_HEADER_TENANT),"blacklistService",b);
                            //.recordBehaviorAndCheck(b);
                        });
                    });
                    asyncResultHandler.handle(succeededFuture(PostPartyAttendResponse.respond201WithApplicationJson(entity,PostPartyAttendResponse.headersFor201())));
                    PartyLogSave.saveLog("批量修改活动名称为【"+ attend.getPartyName() +"】，读者证号为【"+idString+"】的签到记录",3,entity.getMetadata(),entity.getOperator(),okapiHeaders,asyncResultHandler,pg);
                }
            });
        }
    }


    /**
     * Retrieve a list of attend items.
     *
     * @param query              A query expressed as a CQL string
     *                           (see [dev.folio.org/reference/glossary#cql](https://dev.folio.org/reference/glossary#cql))
     *                           using valid searchable fields.
     *                           The first example below shows the general form of a full CQL query,
     *                           but those fields might not be relevant in this context.
     *                           <p>
     *                           with valid searchable fields: for example link = 1234
     * @param offset             Skip over a number of elements by specifying an offset value for the query
     *                           default value: "0"
     *                           minimum value: "0.0"
     *                           maximum value: "2.147483647E9"
     * @param limit              Limit the number of elements returned in the response
     *                           default value: "10"
     *                           minimum value: "0.0"
     *                           maximum value: "2.147483647E9"
     * @param lang               Requested language. Optional. [lang=en]
     *                           <p>
     *                           default value: "en"
     *                           pattern: "[a-zA-Z]{2}"
     * @param okapiHeaders
     * @param asyncResultHandler An AsyncResult<Response> Handler  {@link Handler} which must be called as follows - Note the 'GetPatronsResponse' should be replaced with '[nameOfYourFunction]Response': (example only) <code>asyncResultHandler.handle(io.vertx.core.Future.succeededFuture(GetPatronsResponse.withJsonOK( new ObjectMapper().readValue(reply.result().body().toString(), Patron.class))));</code> in the final callback (most internal callback) of the function.
     * @param vertxContext       The Vertx Context Object <code>io.vertx.core.Context</code>
     */
    @Override
    public void getPartyAttend(String query, int offset, int limit, String lang, Map<String, String> okapiHeaders, Handler<AsyncResult<Response>> asyncResultHandler, Context vertxContext) {
          CQLWrapper cql = null;
          if (!StrUtil.isBlankOrUndefined(query))
          {
              try {
                  cql=PartyUtil.getCQL(query,limit,offset,NOTIFY_TABLE);
              }catch (Exception e)
              {
                  ValidationHelper.handleError(e, asyncResultHandler);
                  return;
              }
          }
          PgUtil.get(NOTIFY_TABLE,AttendGroup.class, AttendCollection.class,cql==null?null:cql.getQuery(),offset,limit,okapiHeaders,vertxContext, GetPartyAttendResponse.class,asyncResultHandler);
    }

    @Override
    public void getPartyAttendToExportExcel(String query, int offset, int limit, Map<String, String> okapiHeaders, Handler<AsyncResult<Response>> asyncResultHandler, Context vertxContext) {
        CQLWrapper cql = null;
        if (!StrUtil.isBlankOrUndefined(query))
        {
            try {
                cql=PartyUtil.getCQL(query,-1,-1,NOTIFY_TABLE);
            }catch (Exception e)
            {
                ValidationHelper.handleError(e, asyncResultHandler);
                return;
            }
        }
        Map<String,String> headerAlias = new LinkedHashMap<>();
        headerAlias.put("partyname","活动名称");
        headerAlias.put("attendstate","考勤状态");
        headerAlias.put("attenddate","签到时间");
        headerAlias.put("partystartdate","活动时间");
        headerAlias.put("readername","读者名称");

        String[] fields = new String[]{"(jsonb #> '{operator}' ->> 'username') as readername",
                "(jsonb->>'partyName') as partyname",
                "(jsonb->>'partyStartDate') as partystartdate ",
                "(jsonb->>'attendDate') as attenddate",
                "(jsonb->>'state') as state",
                "(jsonb->>'attendState') as attendstate"
        };

        PgUtil.postgresClient(vertxContext,okapiHeaders).
                get(NOTIFY_TABLE, AttendExportObject.class,fields,cql,false,false, reply->{
                    if (reply.succeeded()){
                        List<AttendExportObject> list = reply.result().getResults();
                        FileExport.fileExports(headerAlias, list, h -> {
                            if (h.succeeded()) {
                                asyncResultHandler.handle(Future.succeededFuture(GetPartyAttendToExportExcelResponse.respond200WithApplicationOctetStream(h.result())));
                            } else {
                                ValidationHelper.handleError(reply.cause(), asyncResultHandler);
                            }
                        });
                    }else{
                        logger.info(reply.cause());
                        //   ValidationHelper.handleError(reply.cause(), asyncResultHandler);
                    }
                });
    }


    /**
     * Retrieve attend item with given {attendId}
     *
     * @param id
     * @param lang               Requested language. Optional. [lang=en]
     *                           <p>
     *                           default value: "en"
     *                           pattern: "[a-zA-Z]{2}"
     * @param okapiHeaders
     * @param asyncResultHandler An AsyncResult<Response> Handler  {@link Handler} which must be called as follows - Note the 'GetPatronsResponse' should be replaced with '[nameOfYourFunction]Response': (example only) <code>asyncResultHandler.handle(io.vertx.core.Future.succeededFuture(GetPatronsResponse.withJsonOK( new ObjectMapper().readValue(reply.result().body().toString(), Patron.class))));</code> in the final callback (most internal callback) of the function.
     * @param vertxContext       The Vertx Context Object <code>io.vertx.core.Context</code>
     */
    @Override
    public void getPartyAttendById(String id, String lang, Map<String, String> okapiHeaders, Handler<AsyncResult<Response>> asyncResultHandler, Context vertxContext) {
          if (!StrUtil.isBlankOrUndefined(id))
          {
              logger.error("The  id  is null , check your request id" );
              Errors valErr = ValidationHelper.createValidationErrorMessage("id", id,
                      "Your request ID is empty, this is not allowed  ");
              asyncResultHandler.handle(succeededFuture(GetPartyAttendByIdResponse.respond404WithTextPlain(valErr)));
              return;
          }
          PgUtil.getById(NOTIFY_TABLE,Attend.class,id,okapiHeaders,vertxContext,GetPartyAttendByIdResponse.class,asyncResultHandler);
    }

    @Override
    public void deletePartyAttendById(String id, String lang, Map<String, String> okapiHeaders, Handler<AsyncResult<Response>> asyncResultHandler, Context vertxContext) {
        if (StrUtil.isBlankOrUndefined(id)) {
            logger.error("The  id  is null , check your request id");
            Errors valErr = ValidationHelper.createValidationErrorMessage("id", id,
                    "Your request ID is empty, this is not allowed  ");
            asyncResultHandler.handle(succeededFuture(PutPartyAttendByIdResponse.respond404WithTextPlain(valErr)));
            return;
        }
        PgUtil.deleteById(NOTIFY_TABLE, id, okapiHeaders, vertxContext, DeletePartyAttendByIdResponse.class, asyncResultHandler);
    }


    @Override
    public void putPartyAttendById(String id, String lang, org.folio.rest.jaxrs.model.Attend entity, Map<String, String> okapiHeaders, Handler<AsyncResult<Response>> asyncResultHandler, Context vertxContext) {
            if (StrUtil.isBlankOrUndefined(id))
            {
                logger.error("The  id  is null , check your request id" );
                Errors valErr = ValidationHelper.createValidationErrorMessage("id", id,
                        "Your request ID is empty, this is not allowed  ");
                asyncResultHandler.handle(succeededFuture(PutPartyAttendByIdResponse.respond404WithTextPlain(valErr)));
                return;
            }
            PgUtil.put(NOTIFY_TABLE,entity,id,okapiHeaders,vertxContext,PutPartyAttendByIdResponse.class,reply->{
                //BlacklistService.recordBehaviorAndCheck(entity);
                TenantsTaskService.setJob(okapiHeaders.get(RestVerticle.OKAPI_HEADER_TENANT),"blacklistService",entity);
                asyncResultHandler.handle( succeededFuture(PutPartyAttendByIdResponse.respond204()));
            });

    }



    /**
     * @param entity             <code>org.folio.rest.jaxrs.model.Attend</code>
     *                           {
     *                           "readId": 971460036650480,
     *                           "category": "null",
     *                           "readName": "koss",
     *                           "activityId": "4071d8dc-b580-49c7-b273-0c56a5b82159",
     *                           "activityName": "测试活动",
     *                           "activityDate": "yyyy-MM-dd HH:mm:ss",
     *                           "examineState": 1,
     *                           "registerDate": null,
     *                           "attendanceState": 1,
     *                           "reserveId": "4071d8dc-b580-49c7-b273-0c56a5b82159"
     *                           }
     * @param okapiHeaders
     * @param asyncResultHandler An AsyncResult<Response> Handler  {@link Handler} which must be called as follows - Note the 'GetPatronsResponse' should be replaced with '[nameOfYourFunction]Response': (example only) <code>asyncResultHandler.handle(io.vertx.core.Future.succeededFuture(GetPatronsResponse.withJsonOK( new ObjectMapper().readValue(reply.result().body().toString(), Patron.class))));</code> in the final callback (most internal callback) of the function.
     * @param vertxContext       The Vertx Context Object <code>io.vertx.core.Context</code>
     */
    @Override
    public void postPartyAttendAttendBatchUpdate(Attend entity, Map<String, String> okapiHeaders, Handler<AsyncResult<Response>> asyncResultHandler, Context vertxContext) {
        if (entity.getAttendState() ==null || StrUtil.isBlankOrUndefined(entity.getIdArray()))
        {
            logger.error("the  id is null , check your request id" );
            Errors valErr = ValidationHelper.createValidationErrorMessage("id", entity.getIdArray(),
                    "Your request ID is empty, this is not allowed  ");
            asyncResultHandler.handle(succeededFuture(PostPartyAttendAttendBatchUpdateResponse.respond400WithTextPlain(valErr)));
            return;
        }
        String [] idArray = entity.getIdArray().split(",");
        JsonArray jsonArray = new JsonArray(Arrays.asList(idArray));
        PostgresClient pg = PgUtil.postgresClient(vertxContext, okapiHeaders);
        pg.getById(NOTIFY_TABLE,jsonArray,Attend.class,reply->{
            if(reply.succeeded())
            {
                Map<String, Attend> attendGroupList = reply.result();
                if (attendGroupList.size() == 0)
                {
                    asyncResultHandler.handle(succeededFuture(PostPartyAttendAttendBatchUpdateResponse.respond400WithTextPlain(entity)));
                }
              attendGroupList.values().forEach(a->{
                   a.setAttendState(entity.getAttendState());
                   pg.update(NOTIFY_TABLE,a,a.getId(),re ->{
                       TenantsTaskService.setJob(okapiHeaders.get(RestVerticle.OKAPI_HEADER_TENANT),"blacklistService",a);
                      // BlacklistService.recordBehaviorAndCheck(a);
                   });
                });
                asyncResultHandler.handle(succeededFuture(PostPartyAttendAttendBatchUpdateResponse.respond201WithApplicationJson(entity,PostPartyAttendAttendBatchUpdateResponse.headersFor201())));
            }else {
                ValidationHelper.handleError(reply.cause(), asyncResultHandler);
            }
        });


    }


}
